﻿using System;
using Microsoft.Practices.Unity.InterceptionExtension;
using Sapit.Base;
using Sapit.Helpers;
using Sapit.Unity.Attributes;

namespace Sapit.Unity
{
    public class ResponseTimeThrottleHandler : ResponseTimeThrottleBase, ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            IMethodReturn msg;

            Invocation inv = new Invocation() { InvocationTarget = input.Target, Method = input.MethodBase };

            var responseTimeThrottle = AttributeHelper.GetAttributeIfPresent<ResponseTimeThrottleAttribute>(input.MethodBase);
            if (responseTimeThrottle != null)
            {
                    if (IsThisInvocationThrottled(inv))
                    {
                        DecrementThrottleCounterForThisInvocation(inv);

                        switch (responseTimeThrottle.BehaviorWhenThrottling)
                        {
                            case Behavior.Undefined:
                            case Behavior.ReturnDefaultValue:
                                msg = input.CreateMethodReturn(responseTimeThrottle.ThrottleReturnValue);
                                break;
                            case Behavior.ThrowException:
                                msg = input.CreateExceptionMethodReturn(new InvalidOperationException(string.Format("{0}", responseTimeThrottle.ThrottleReturnValue)));
                                break;
                            default:
                                msg = input.CreateMethodReturn(responseTimeThrottle.ThrottleReturnValue);
                                break;
                        }
                    }
                    else
                    {
                        UnThrottleThisInvocation(inv);
                        double responseTime = InvokeMethodAndRecordResponseTime(input, getNext, out msg);
                        if (responseTime > responseTimeThrottle.ThrottleThresholdMilliseconds)
                        {
                            ThrottleThisInvocation(inv, responseTimeThrottle.SkipCallsWhenThrottling);
                        }
                    }
            }
            else
            {
                msg = getNext()(input, getNext);
            }

            return msg;
        }

        private static double InvokeMethodAndRecordResponseTime(IMethodInvocation invocation, GetNextHandlerDelegate getNext, out IMethodReturn msg)
        {
            DateTime beforeInvocation = DateTime.Now;
            msg = getNext()(invocation, getNext);
            DateTime afterInvocation = DateTime.Now;
            return (afterInvocation - beforeInvocation).TotalMilliseconds;
        }

        public int Order { get; set; }
    }
}
